﻿// <copyright file="Transform3DGroup.cs" company="Axelerate Solutions S.A.">
// Copyright (c) Axelerate Solutions S.A.  All rights reserved.
// </copyright>

namespace Axelerate.Silverlight3D.Media.Media3D
{
    using System;
    using System.Net;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Documents;
    using System.Windows.Ink;
    using System.Windows.Input;
    using System.Windows.Media;
    using System.Windows.Media.Animation;
    using System.Windows.Shapes;
    using System.Windows.Markup;

    /// <summary>
    /// File name: Transform3DGroup.cs
    /// Company: Axelerate Solutions S.A.
    /// Created By: Daniel Rojas
    /// Created Date: 10/09/2008 10:50:00 h.
    /// Description: Represents a transformation that is a composite of the Transform3D children in its Transform3DCollection.
    ///   <Modifications>
    ///   </Modifications>
    /// </summary>
    /// <remarks>This class is useful when grouping transformations that are applied to some objects in a scene but not others. It also allows developers to re-use transformations by defining them once and then adding them to different Transform3DGroup objects.</remarks>
    [ContentPropertyAttribute("Children")]
    public sealed class Transform3DGroup : Transform3D
    {
        #region Fields
        
        /// <summary>
        /// Identifies the Children dependency property.
        /// </summary>
        public static readonly DependencyProperty ChildrenProperty =
                    DependencyProperty.Register(
                          "Children",
                          typeof(Transform3DCollection),
                          typeof(Transform3DGroup),
                          new PropertyMetadata(OnChildrenPropertyChanged));
        /// <summary>
        /// cache the matrix based on his child trasnformations.
        /// </summary>
        private Matrix3D innerTransform;
        #endregion

        #region Properties
        public Transform3DCollection Children
        {
            get
            {

                return (Transform3DCollection)this.GetValue(ChildrenProperty);
            }
            set
            {
                this.SetValue(ChildrenProperty, value);
                value.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(value_CollectionChanged);
            }
        }

        /// <summary>
        /// Handle changes on the Children collections.
        /// </summary>
        /// <param name="sender">sender of the event</param>
        /// <param name="e">NotifyCollectionChangedEventArgs with the information of the change.</param>
        void value_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.OldItems != null)
            {
                foreach (Transform3D item in e.OldItems)
                {
                    item.PropertyChanged -= new System.ComponentModel.PropertyChangedEventHandler(item_PropertyChanged);
                }
            }
            if (e.NewItems != null)
            {
                foreach (Transform3D item in e.NewItems)
                {
                    item.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(item_PropertyChanged);
                }
            }
            this.CalculateInnerMatrix();
            this.NotifyPropertyChanged("Children");

        }

        void item_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            this.CalculateInnerMatrix();
            this.NotifyPropertyChanged("Children");
        }

        #endregion

        public Transform3DGroup()
        {
            this.Children = new Transform3DCollection();
        }

        #region Methods
        
        /// <summary>
        /// Raise the Children changed event
        /// </summary>
        /// <param name="d">DependencyObject owner of the property</param>
        /// <param name="e">DependencyPropertyChangedEventArgs with the necesary information</param>
        private static void OnChildrenPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((Transform3DGroup)d).NotifyPropertyChanged("Children");
        }

        /// <summary>
        /// Gets a Matrix3D that indicates the current transform value.
        /// </summary>
        /// <value>Matrix3D that indicates the current transform value.</value>
        public override Matrix3D Value
        {
            get
            {
                return this.innerTransform;
            }
        }

        private void CalculateInnerMatrix()
        {
            if ((this.Children == null) ||
                     (this.Children.Count == 0))
            {
                innerTransform = Matrix3D.Identity;
            }

            Matrix3D result = Matrix3D.Identity;
            foreach (Transform3D transform in this.Children)
            {
                result.Append(transform.Value);
            }
            innerTransform = result;
        }

        /// <summary>
        /// Gets a value that indicates whether the transformation is affine.
        /// </summary>
        /// <value>True if the transformation is affine; false otherwise.</value>
        /// <remarks>This property require calculate the full transform matrix before say if is affine.</remarks>
        public override bool IsAffine
        {
            get
            {
                return Value.IsAffine;
            }
        }
        #endregion

        

    }
}
